The Leaf dataset¶




¿De qué trata este dataset?¶


Este conjunto de datos consiste en una colección de características que mide forma y textura extraídas de imágenes digitales de especímenes de hojas procedentes de un total de 40 especies de plantas diferentes. Fue recolectado en Febrero del 2014 en la Facultad de Ciencias, Universidade do Porto, Portugal. https://archive.ics.uci.edu/ml/datasets/leaf

A continuacion algunas de estas especies:


Aspecto general de una hoja de cada una de las 40 especies de plantas.¶


In [41]:
from IPython.display import Image
Image("plants.png", width=650, height=650)
Out[41]:


Foto de la specie 40: Fragaria Vesca¶


In [42]:
from IPython.display import Image
Image("RGB/40. Fragaria vesca/iPAD2_C40_EX01.JPG", width=350, height=350)
Out[42]:


Foto de la especie 15: Populus Alba¶


In [43]:
Image("RGB/15. Populus alba/iPAD2_C15_EX06.JPG", width=350, height=350)## Especie 40: Fragaria Vesca
Out[43]:


Nombre científico y número de hojas recolectadas de cada una de las 40 especies de plantas.¶


In [44]:
Image("description.png", width=550, height=550)
Out[44]:



¿Cómo es la data?¶


Habitualmente la data se representa mediante tablas, donde cada columna es una característica de la data y cada fila es un registro. Cada característica es una medición distinta de las demas. Y digamos, por ejemplo, que cada registro representa los datos medidos en un tiempo determinado, por ejemplo, la primera fila a la 1:00pm, la segunda 1:10pm, la tercera 1:15pm, etc.
Pasos:

  1. Importamos la librería pandas, la cual es una herramienta para el data análisis y trabaja con DataFrames, una estructura de datos eficiente para el trabajo con datos.
  2. Leemos el dataset desde un archivo .csv.
  3. Mostramos su forma
  4. Mostramos las 10 primeras filas

In [45]:
import pandas as pd #1
names = ['species', 'specimen_number', 'eccentricity', 'aspect_ratio', 
        'elongation', 'solidity', 'stochastic_convexity', 'isoperimetric_factor', 
        'maximal_indentation_depth', 'lobedness', 'average_intensity', 
        'average_contrast', 'smoothness', 'third_moment', 'uniformity', 'entropy']

leaf = pd.read_csv("leaf.csv", names=names) #2
print('Filas y columnas', leaf.shape) #3
leaf.head(10) #4
('Filas y columnas', (340, 16))
Out[45]:
species specimen_number eccentricity aspect_ratio elongation solidity stochastic_convexity isoperimetric_factor maximal_indentation_depth lobedness average_intensity average_contrast smoothness third_moment uniformity entropy
0 1 1 0.72694 1.4742 0.32396 0.98535 1.00000 0.83592 0.004657 0.003947 0.047790 0.127950 0.016108 0.005232 0.000275 1.17560
1 1 2 0.74173 1.5257 0.36116 0.98152 0.99825 0.79867 0.005242 0.005002 0.024160 0.090476 0.008119 0.002708 0.000075 0.69659
2 1 3 0.76722 1.5725 0.38998 0.97755 1.00000 0.80812 0.007457 0.010121 0.011897 0.057445 0.003289 0.000921 0.000038 0.44348
3 1 4 0.73797 1.4597 0.35376 0.97566 1.00000 0.81697 0.006877 0.008607 0.015950 0.065491 0.004271 0.001154 0.000066 0.58785
4 1 5 0.82301 1.7707 0.44462 0.97698 1.00000 0.75493 0.007428 0.010042 0.007938 0.045339 0.002051 0.000560 0.000024 0.34214
5 1 6 0.72997 1.4892 0.34284 0.98755 1.00000 0.84482 0.004945 0.004451 0.010487 0.058528 0.003414 0.001125 0.000025 0.34068
6 1 7 0.82063 1.7529 0.44458 0.97964 0.99649 0.76770 0.005928 0.006395 0.018375 0.080587 0.006452 0.002271 0.000041 0.53904
7 1 8 0.77982 1.6215 0.39222 0.98512 0.99825 0.80816 0.005099 0.004731 0.024875 0.089686 0.007979 0.002466 0.000147 0.66975
8 1 9 0.83089 1.8199 0.45693 0.98240 1.00000 0.77106 0.006005 0.006564 0.007245 0.040616 0.001647 0.000388 0.000033 0.33696
9 1 10 0.90631 2.3906 0.58336 0.97683 0.99825 0.66419 0.008402 0.012848 0.007010 0.042347 0.001790 0.000459 0.000028 0.28082
  1. Class (Species)
  2. Specimen Number
  3. Eccentricity
  4. Aspect Ratio
  5. Elongation
  6. Solidity
  7. Stochastic Convexity
  8. Isoperimetric Factor
  9. Maximal Indentation Depth
  10. Lobedness
  11. Average Intensity
  12. Average Contrast
  13. Smoothness
  14. Third moment
  15. Uniformity
  16. Entropy



Entendamos la data¶



Veamos algunos de sus características (columnas) :

'eccentricity', 'aspect_ratio', 'elongation', 'solidity'


Usaremos distintos gráficos para analizar las distribución de cada una de las columnas.



Histogramas¶


Los histogramas son gráficos en forma de barras, que muestran la frecuencia de un determinado intervalo de valores, es usado para variables continuas.

In [46]:
Image("explain_histograms.png", width=650, height=650)
Out[46]:



Grafiquemos las primeras cuatro caracteristicas¶

  1. Eccentricity
  2. Aspect Ratio
  3. Elongation
  4. Solidity


In [47]:
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns

subset = ['eccentricity', 'aspect_ratio', 'elongation', 'solidity']
hist = leaf[subset] \
        .plot(kind='hist', figsize=(5, 5), subplots=False, sharex=False)
_ = plt.xlabel("Values")

Se observa que no se logra visualizar correctamente todas las distribuciones, por tanto, se grafica por separado.


Variable: eccentricity¶


In [48]:
hist = leaf.eccentricity \
        .plot(kind='hist', figsize=(5, 5), subplots=False, sharex=False)
_ = plt.xlabel("Values")


Variable: aspect_ratio¶


In [49]:
hist = leaf.aspect_ratio \
        .plot(kind='hist', figsize=(5, 5), subplots=False, sharex=False)
_ = plt.xlabel("Values")


Variable: elongation¶


In [50]:
hist = leaf.elongation \
        .plot(kind='hist', figsize=(5, 5), subplots=False, sharex=False)
_ = plt.xlabel("Values")


Variable: solidity¶


In [51]:
hist = leaf.solidity \
        .plot(kind='hist', figsize=(5, 5), subplots=False, sharex=False)
_ = plt.xlabel("Values")



Gráfica de densidad de todas las variables¶


In [52]:
_ = leaf.plot(kind='density', figsize=(10, 10), subplots=True, sharex=False)


Diagrama de caja o boxplot¶


Sirven también para ver la distribución de los datos, pero su utilidad se enfoca en ver si hay valores atípicos o outliers, ver la dispersión de los valores y analizar los datos del histograma en una sola dimensión.

In [53]:
Image("explain_boxplots.png", width=550, height=550)
Out[53]:
In [54]:
Image("explain_boxplots2.png", width=350, height=350)
Out[54]:
In [55]:
_ = leaf.plot(kind='box',figsize=(10, 10), vert=False)
_ = plt.xlabel("Values")


Analizando cada característica para cada especie¶

Box Plots¶


species vs eccentricity¶


In [56]:
plt.figure(figsize=(10,10))
_ = sns.boxplot(x="species", y="eccentricity", data=leaf)
_ = sns.stripplot(x="species", y="eccentricity", data=leaf, jitter=True, edgecolor="gray", size=5)


species vs aspect_ratio¶


In [57]:
plt.figure(figsize=(10,10))
_ = sns.boxplot(x="species", y="aspect_ratio", data=leaf)
_ = sns.stripplot(x="species", y="aspect_ratio", data=leaf, jitter=True, edgecolor="gray")


species vs elongation¶


In [58]:
plt.figure(figsize=(10,10))
_ = sns.boxplot(x="species", y="elongation", data=leaf)
_ = sns.stripplot(x="species", y="elongation", data=leaf, jitter=True, edgecolor="gray")

Histogramas en grupos¶


Tal como se vio anteriormente, graficar todos los histogramas para cada una de las clases puede dar lugar a un solapamiento de distribuciones, por tanto, tomaremos las primeras 50 filas por un tema de claridad.


species vs eccentricity¶


In [59]:
_ = sns.FacetGrid(leaf.head(50), hue="species", size=6) \
   .map(sns.distplot, "eccentricity") \
   .add_legend()


species vs aspect_ratio¶


In [60]:
_ = sns.FacetGrid(leaf.head(50), hue="species", size=6) \
   .map(sns.distplot, "aspect_ratio") \
   .add_legend()


species vs elongation¶


In [61]:
_ = sns.FacetGrid(leaf.head(50), hue="species", size=6) \
   .map(sns.distplot, "elongation") \
   .add_legend()



Analizando patrones¶


Con los siguientes gráficos podemos observar cúmulos o grupos definidos, donde cada grupo es una clase. Por tanto, el problema trata de clasificación, especificamente, clasificación múltiple supervisada.

Scatter plot¶


In [62]:
leaf_subset = leaf[subset + ['species']].head(50)
leaf_subset.head()
Out[62]:
eccentricity aspect_ratio elongation solidity species
0 0.72694 1.4742 0.32396 0.98535 1
1 0.74173 1.5257 0.36116 0.98152 1
2 0.76722 1.5725 0.38998 0.97755 1
3 0.73797 1.4597 0.35376 0.97566 1
4 0.82301 1.7707 0.44462 0.97698 1
In [63]:
_ = sns.pairplot(leaf_subset, hue='species', size=2)


Andrews curve¶



Es una manera de expresar un dataset, de dimensión alta, en grupos fácilmente identificables.

In [64]:
from pandas.tools.plotting import andrews_curves
plt.figure(figsize=(10,10))
_ = andrews_curves(leaf_subset, "species")


Kdeplot¶



Resalta con mayor claridad los clusters o clases encontrados.

In [65]:
specie_1 = leaf_subset.query("species == 1")
specie_2 = leaf_subset.query("species == 2")
specie_3 = leaf_subset.query("species == 3")
specie_4 = leaf_subset.query("species == 4")

f, ax = plt.subplots(figsize=(12, 12))
# ax.set_aspect("equal")


ax = sns.kdeplot(specie_1.solidity, specie_1.elongation,
                 cmap="Blues", shade=True, shade_lowest=False)
ax = sns.kdeplot(specie_2.solidity, specie_2.elongation,
                 cmap="Reds", shade=True, shade_lowest=False)
ax = sns.kdeplot(specie_3.solidity, specie_3.elongation,
                 cmap="Greens", shade=True, shade_lowest=False)
ax = sns.kdeplot(specie_4.solidity, specie_4.elongation,
                 cmap="Oranges", shade=True, shade_lowest=False)

blue = sns.color_palette("Blues")[-2]
red = sns.color_palette("Reds")[-2]
green = sns.color_palette("Greens")[-2]
orange = sns.color_palette("Oranges")[-2]

_ = ax.text(0.94, 0.45, "specie 1: Quercus suber", size=16, color=blue)
_ = ax.text(0.94, 0.55, "specie 2: Salix atrocinera", size=16, color=red)
_ = ax.text(0.90, 0.25, "specie 3: Populus nigra", size=16, color=green)
_ = ax.text(0.96, 0.08, "specie 4: Alnus sp.", size=16, color=orange)



Matriz de correlaciones¶


Muestra la correlación (directa o inversa) que existen entre las características o columnas, excepto la variable de respuesta. Esto sirve para ver si es posible resumir columnas (reducir), aumentar o combinarlas con el objetivo de reducir el tiempo de entrenamiento, eliminar redundancia de datos y mejorar el performance del modelo.

In [66]:
%%time
from collections import OrderedDict

class_name = 'species'
features = leaf.ix[:, leaf.columns != class_name]
dictFI = OrderedDict(features)

features = dictFI.keys()
features.append('species')

def plot_heatmap(df):
    fig, axes = plt.subplots(figsize=(10,10))
    _ = sns.heatmap(df, annot=True)

    
plot_heatmap(leaf[features].corr(method='pearson'))
CPU times: user 912 ms, sys: 12 ms, total: 924 ms
Wall time: 922 ms



Vamos más en detalle, analicemos la variable 'species'¶


Verificamos la cantidad de valores NaN : Not a Number

In [67]:
from __future__ import division

print 'filas sin valores nan: ', len(leaf.species.dropna())
print 'filas totales: ', len(leaf.species)
filas sin valores nan:  340
filas totales:  340
In [68]:
print(leaf.groupby('species').size())
species
1     12
2     10
3     10
4      8
5     12
6      8
7     10
8     11
9     14
10    13
11    16
12    12
13    13
14    12
15    10
22    12
23    11
24    13
25     9
26    12
27    11
28    12
29    12
30    12
31    11
32    11
33    11
34    11
35    11
36    10
dtype: int64

Selección de características o Feature Selection¶



Feature Selection es un proceso donde se selecciona automáticamente las características que contribuyen en mayor medida a la variable a predecir.

Hay cuatro tipos importantes:¶

  1. Univariate Selection.
  2. Recursive Feature Elimination.
  3. Principle Component Analysis.
  4. Feature Importance

Beneficios¶

Reduces Overfitting: El modelo funcionará bien con data nunca vista.

Improves Accuracy: Datos menos engañosos significa una mejora en la exactitud del modelo.

Reduces Training Time: Menos data significa que los algoritmos entrenarán más rapido.



Univariate Selection¶



Selecciona las columnas que tengan una relación fuerte con la variable a predecir.

Para regresión: f_regression

Para classificación: chi2 or f_classif

En este caso de clasificación, se realiza el test chi square, el cual mide la dependencia entre variables estocásticas, este test remueve las características más probables a ser independientes, por lo tanto, irrelevantes para la clasificación.

In [69]:
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

class_name = 'species'
X = leaf.ix[:, leaf.columns != class_name]
X = leaf.ix[:, leaf.columns != 'specimen_number']
y = leaf[class_name]


test = SelectKBest(score_func=chi2, k=4)
fit = test.fit(X, y)

print fit.scores_

print

X.head()
[  2.27373117e+03   1.75897827e+01   8.85082265e+02   2.31834450e+01
   4.58587452e+00   4.22479628e+00   2.81956691e+01   1.08878700e+01
   4.81637651e+02   6.24689412e+00   5.69313034e+00   2.74745718e+00
   1.14425686e+00   8.43013743e-02   7.15759454e+01]

Out[69]:
species eccentricity aspect_ratio elongation solidity stochastic_convexity isoperimetric_factor maximal_indentation_depth lobedness average_intensity average_contrast smoothness third_moment uniformity entropy
0 1 0.72694 1.4742 0.32396 0.98535 1.00000 0.83592 0.004657 0.003947 0.047790 0.127950 0.016108 0.005232 0.000275 1.17560
1 1 0.74173 1.5257 0.36116 0.98152 0.99825 0.79867 0.005242 0.005002 0.024160 0.090476 0.008119 0.002708 0.000075 0.69659
2 1 0.76722 1.5725 0.38998 0.97755 1.00000 0.80812 0.007457 0.010121 0.011897 0.057445 0.003289 0.000921 0.000038 0.44348
3 1 0.73797 1.4597 0.35376 0.97566 1.00000 0.81697 0.006877 0.008607 0.015950 0.065491 0.004271 0.001154 0.000066 0.58785
4 1 0.82301 1.7707 0.44462 0.97698 1.00000 0.75493 0.007428 0.010042 0.007938 0.045339 0.002051 0.000560 0.000024 0.34214



Feature Importance¶



Los Bagged decision trees como lo son Random Forest y Extra Trees pueden ser usados para estimar la importancia de caracteristicas. Asi como tambien, el algoritmo Gradient Boosting.

In [70]:
from sklearn.model_selection import cross_val_score

def modelfit(alg, dtrain, predictors, performCV=True, printFeatureImportance=True, cv_folds=5):
    alg.fit(dtrain[predictors], dtrain['species'])
        
    dtrain_predictions = alg.predict(dtrain[predictors])
    dtrain_predprob = alg.predict_proba(dtrain[predictors])[:,1]
    
    if printFeatureImportance:
        feat_imp = pd.Series(alg.feature_importances_, predictors).sort_values(ascending=False)
        feat_imp.plot(kind='bar', title='Feature Importances', figsize=(14.,7.))
        plt.ylabel('Feature Importance Score')
        plt.show()
        plt.close()
    
    return feat_imp



Probamos primero con ExtraTreesClassifier¶



In [71]:
%%time
from sklearn.ensemble import ExtraTreesClassifier
target = ['species', 'specimen_number']


predictors = [x for x in leaf if x not in target]
model = ExtraTreesClassifier(random_state=10)
fi = modelfit(model, leaf, predictors)
print fi.head(6)
isoperimetric_factor         0.105883
solidity                     0.105637
aspect_ratio                 0.085291
eccentricity                 0.081751
elongation                   0.079355
maximal_indentation_depth    0.071818
dtype: float64
CPU times: user 508 ms, sys: 4 ms, total: 512 ms
Wall time: 510 ms



Probamos ahora con GradientBoostingClassifier¶



In [72]:
%%time
from sklearn.ensemble import GradientBoostingClassifier
target = ['species', 'specimen_number']


predictors = [x for x in leaf if x not in target]
model = GradientBoostingClassifier(random_state=10)
fi = modelfit(model, leaf, predictors)
print fi.head(6)
solidity                0.070932
elongation              0.066733
isoperimetric_factor    0.062637
aspect_ratio            0.056867
eccentricity            0.046703
uniformity              0.040707
dtype: float64
CPU times: user 4.83 s, sys: 16 ms, total: 4.85 s
Wall time: 4.87 s



Cómo dividir el dataset para mejorar el rendimiento de un algoritmo de Machine Learning¶



En este apartado se aprenderá cómo evaluar un algoritmo, mediante diferentes técnicas de estimacion:

  • Train y Test Sets

  • K-fold Cross Validation

  • Leave One Out Cross Validation

  • Repeated Random Test-Train Splits </ul>



Split into Train and Test Sets¶



Esta técnica es usada para grandes datasets, donde el entrenamiento tenga un coste alto en recursos y tiempo. Por contra, esta técnica puede tener inducir a una alta varianza, es decir, las diferencias entre la data de entrenamiento (training) y de prueba (testing) pueden ser significativas a la hora de estimar la precisión del modelo.

Lo más comun es dividir la data en: 67% para el training y 33% para el testing.

In [73]:
Image("split_train_test.png", width=450, height=450)
Out[73]:
In [74]:
%%time
from sklearn.linear_model import LogisticRegression
from sklearn import model_selection

test_size = 0.33
seed = 7
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y,
                                                                    test_size=test_size, random_state=seed)
model = LogisticRegression()
model.fit(X_train, y_train)
result = model.score(X_test, y_test)

print("Accuracy: %.3f%%") % (result*100.0)
Accuracy: 56.637%
CPU times: user 52 ms, sys: 0 ns, total: 52 ms
Wall time: 49.6 ms

El seed o semilla es fijo para tener el mismo conjunto de datos en todo el proceso, de tal manera, que cuando se haga pruebas usando otro algoritmo, se pueda usar la misma dataset.



K-fold Cross Validation¶



Esta técnica divide la data en k conjuntos, de tal manera que se entrena con los k-1 folds(pliegues) y se testea con el sobrante, luego se hace el mismo procedimiento escogiendo otros k-1 . Este procedimiento se realiza hasta que cada fold es testeado. Luego de tener las k puntuaciones de rendimiento se puede resumir usando la media o la desviación estandar. Este resultado es más fiable en el rendimiento del algoritmo cuando se presenta una nueva data. Es necesario que la elección de k permita que las muestras sean lo suficientemente grande, lo valores más comúnes para k son 3, 5, 10.

In [75]:
Image("K-fold_cross_validation.jpg", width=650, height=650)
Out[75]:
In [76]:
%%time
from sklearn.cross_validation import KFold
from sklearn.model_selection import cross_val_score

num_folds = 10
num_instances = len(X)
seed = 7
# scoring = 'roc_auc'

kfold = KFold(n=num_instances, n_folds=num_folds,
                              random_state=seed)
model = LogisticRegression()
results = cross_val_score(model, X, y, cv=kfold)

#model.fit(X_train, Y_train)
#result = model.score(X_test, Y_test)
print("Accuracy: mean : %.3f%%, SD : %.3f%%") % (results.mean()*100.0, results.std()*100.0)
Accuracy: mean : 2.647%, SD : 2.443%
CPU times: user 584 ms, sys: 4 ms, total: 588 ms
Wall time: 590 ms



Leave One Out Cross Validation¶



Esta técnica trabaja con k = len(X), con el objetivo de estimar con una mayor precisión el modelo para datos nuevos. Como desventaja, es computacionalmente costoso que la técnica cross validation y su varianza es alta.

In [77]:
%%time
from sklearn.cross_validation import LeaveOneOut


num_instances = len(X)

loocv = LeaveOneOut(n=num_instances)

model = LogisticRegression()
results = cross_val_score(model, X, y, cv=loocv)

#model.fit(X_train, Y_train)
#result = model.score(X_test, Y_test)
print("Accuracy: mean : %.3f%%, SD : %.3f%%") % (results.mean()*100.0, results.std()*100.0)
Accuracy: mean : 68.824%, SD : 46.321%
CPU times: user 23.5 s, sys: 12 ms, total: 23.5 s
Wall time: 23.5 s



Stratified Shuffle Split¶



Esta técnica se usa cuando las clases no están balanceadas, es decir, cuando se tiene diferente cantidades de registros de cada clase.

In [78]:
%%time
import numpy as np
from sklearn.cross_validation import StratifiedShuffleSplit

X_array = np.array(X)
y_array = np.array(y)

sss = StratifiedShuffleSplit(y_array, n_iter=1, test_size=0.3, random_state=0)

for train_index, test_index in sss:

    X_train, X_test = X_array[train_index], X_array[test_index]
    y_train, y_test = y_array[train_index], y_array[test_index]
    
model = LogisticRegression()
model.fit(X_train, y_train)
result = model.score(X_test, y_test)

print("Accuracy: %.3f%%") % (result*100.0)
Accuracy: 77.451%
CPU times: user 68 ms, sys: 0 ns, total: 68 ms
Wall time: 78.4 ms



Métricas usadas al evaluar un modelo en Machine Learning¶



En esta parte se verá las diferentes métricas para evaluar la calidad de las predicciones de un modelo de ML. Para la evaluación se usará un k=10 en todas las datasets.


Clasification:¶

  • Dataset: Iris.
  • Algorithm : Logistic Regression.
  • Test Harness: 10-fold Cross validation.


Otro ejemplo podría ser:

Regression:¶

  • Dataset: Boston House Price.
  • Algorithm : Linear Regression.
  • Test Harness: 10-fold Cross validation.


Clasification Metrics¶



  • Classification Accuracy.
  • Logarithmic Loss
  • Area Under ROC Curve.
  • Confusion Matrix.
  • Classification Report.


Clasification Accuracy¶



$$ \text{metric} = \frac{\text{# correct predictions}}{\text{# total predictions}}$$



Es recomendable cuando solo se tiene un número igual de observaciones de cada clase.

In [79]:
num_folds = 10
num_instances = len(X)

seed = 7

kfold = KFold(n=num_instances, n_folds=num_folds,
                              random_state=seed)
model = LogisticRegression()
scoring = 'accuracy'
results = cross_val_score(model, X, y, 
                                           cv=kfold, scoring=scoring)

print("Accuracy: mean : %.3f (%.3f)") % (results.mean()*100, results.std())
Accuracy: mean : 2.647 (0.024)



Confusion Matrix¶



Es una métrica usada para clases que no están balanceadas, por ejemplo, en el dataset Iris tenemos 50 filas de cada especie las cuales se dice que esta balanceadas, pero en muchos otros problemas las clases estan desbalanceadas, por tanto, esta métrica permite saber específicamente cómo se comporta el modelo clase por clase.

Clave predecida vs Clase real¶


In [80]:
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report

model.fit(X_train, y_train)
predictions = model.predict(X_test)
cm = confusion_matrix(y_test, predictions)

print('Cantidad de valores de prueba: ', len(y_test))

df_cm = pd.DataFrame(cm, y.unique(),
                  y.unique())

plt.figure(figsize=(15,15))
ax = plt.axes()
_ = sns.set(font_scale=1.4)
_ = sns.heatmap(df_cm, annot=True,annot_kws={"size": 16}, ax=ax)
('Cantidad de valores de prueba: ', 102)



Modelando la data usando Pipelines¶



El Pipeline es una manera de juntar transformadores (p.e, Scaler, SelectKbest) y estimadores (ExtraTrees, Logistic Regression) en un solo flujo, de tal manera que cada eslabon del pipeline se ejecute secuencialmente hasta lograr el modelo predictivo. Por ejemplo, se puede poner en un flujo los Transformers: Scale, Normalization, etc, y en otro pipeline, transformers para features selections: univariate, pca, etc. y en otro pipeline poner estimadores: PCA, k-means, etc. Entonces con la función FeatureUnion, podemos unir esos tres pipelines para crear un pipeline total, que realiza todos esos pasos como uno solo. Esto ayuda a evitar la perdida de data y la legibilidad.

In [81]:
from sklearn.cross_validation import KFold
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import Normalizer
from sklearn.pipeline import Pipeline
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.pipeline import FeatureUnion
from sklearn.decomposition import PCA
from sklearn.feature_selection import SelectKBest
from sklearn.svm import SVC

transformers = []
transformers.append(( 'standardize' , StandardScaler()))
# # estimators.append(( 'normalizer' , Normalizer()))
# estimators.append(( 'lda' , LinearDiscriminantAnalysis()))
transf_union = FeatureUnion(transformers)
# model = Pipeline(estimators)

# create feature union
features = []
features.append(('transf_union', transf_union))
features.append(( 'pca', PCA(n_components=3)))
features.append(( 'select_best' , SelectKBest(k=3)))
feature_union = FeatureUnion(features)
# create pipeline
estimators = []
estimators.append(( 'feature_union' , feature_union))
estimators.append(( 'SVC' , SVC()))
# estimators.append(('svm' , SVC(kernel="linear")))
model = Pipeline(estimators)

# evaluate pipeline
num_folds = 10
num_instances = len(X)
seed = 7
kfold = KFold(n=num_instances, n_folds=num_folds, random_state=seed)
model.fit(X_train, y_train)
result = model.score(X_test, y_test)
print('Score:', result)
predictions = model.predict(X_test)
# results = cross_val_score(model, X, y, cv=kfold)
# print('Score:', results.mean())
('Score:', 0.97058823529411764)



Confusion Matrix¶


In [82]:
from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_test, predictions)
print('Cantidad de valores de prueba: ', len(y_test))

df_cm = pd.DataFrame(cm, y.unique(),
                  y.unique())

plt.figure(figsize=(15,15))
ax = plt.axes()
_ = sns.set(font_scale=1.4)
_ = sns.heatmap(df_cm, annot=True,annot_kws={"size": 16}, ax=ax)
('Cantidad de valores de prueba: ', 102)



Valor Real vs Valor Predecido¶



In [83]:
import numpy as np
from sklearn.preprocessing import LabelEncoder

# encoder = LabelEncoder()
# y_test = encoder.fit_transform(y_test)
# predictions = encoder.fit_transform(predictions)

#Ploteando

y_test_l = list(y_test)
predictions = list(predictions)

plt.figure(figsize=(10,10))
plt.scatter(y_test_l, predictions, alpha=0.3, color='green', s=300)
plt.plot([min(y_test_l), max(y_test_l)], [min(y_test_l), max(y_test_l)], 
         'k--', color='darkgreen',lw=4, 
         label= 'X: Real value, Y: Predicted value. Score = '+str(result*100)+'%')

plt.xticks(tuple(y.unique()), tuple(y.unique()), rotation=25,
                              fontsize = 10)
plt.yticks(tuple(y.unique()), tuple(y.unique()), rotation=29,
                              fontsize = 10)
plt.legend(loc='best')
plt.grid(True)



Conclusiones¶



  1. Debemos primero preguntarnos, qué problema queremos resolver.
  2. Luego plotear la data cruda y ver relaciones y patrones, para determinar si el problema puede ser tratado como clasificación o regresión.
  3. Una vez teniendo claro las relaciones entre las variables, pasamos a limpiar la data de ruido, anomalías, etc.
  4. Buscamos las columnas que mayor contribuyan al modelo, tanto en tiempo de entrenamiento y bondad (score).
  5. Buscar la mejor manera de dividir los datos, no siempre un método va a funcionar para todos los casos.
  6. Al crear pipelines de todos los pasos, se reduce la perdida de datos, mejora la precisión y además se mantiene una estructura modular y flexible para la ejecución del cross validación y posteriormente para el análisis de hiperparámetros.
  7. Escoger las adecuadas métricas, roc_auc, accuracy, confusion matrix, etc.
  8. Para aplicaciones de producción, los pasos anteriores se realizan muchas veces, siguiendo un ciclo, de tal manera que se pueda pulir todos los detalles del modelo, haciendo confiable, robusto y eficiente.
  9. No todo se rige al modelo que tenga mayor score, muchas veces se requiere de un modelo explicado, que puede tener menos score pero puede explicar paso a paso sus salidas, como los árboles de decisión.
  10. Por último, el data science es un arte, y como tal, depende mucho de la creatividad del analista para obtener una materia prima muy fina para el posterior entrenamiento. Más limpia y significante es la data, mejor será tu modelo.